Written by Steve Scherrer - July/August 2021

Background

This notebook documents preliminary analysis of tracking data for fish tagged in Molokini Crater between 2020-05-16 and 2021-05-24.

The purpose of this study is to understand how human impacts affect the fish of Molokini Crater

We are particularly interested in answering the following hypotheses: 1. Is the presence of fish affected by vessel presence

  1. Does the proportion of time fish are present within the crater negatively correlated with vessel presence?

Proposed Approach: 1. Begin by calculating the number of each species tagged and basic summary statistics 2. Calculate Metrics - Receiver Use - Pianka’s Niche Overlap - residency 3. Make the following plots - Map - Receiver locations - Map - Average receiver use by Species - Scatterplot - day night plots - Bar Plot - The number of detections per day (individual) - Bar Plot - The number of individuals detected (species) - Line Chart - The proportion of individuals detected n days after tagging (30 day moving average by species) - Bar Plot - Daily vessel traffic - Scatter Plot - vessel traffic vs. proportion of fish detected in crater daily (scatterplot by species) 4. Perform the following statistical Tests - Compare Residency Rates by Species - Compare residency by species, size, and time at liberty - Create a GLM comparing # of individuals in crater regressed against boat traffic and species using AR(1) term on dependent variable on some time scale (daily? 6 hours? depends on resolution of vessel data)

Workspace Setup

Establish Directory Heirarchy

project_directory = '/Users/stephenscherrer/Documents/Programming/Projects/Molokini'
scripts_directory = file.path(project_directory, 'Analysis Scripts')
data_directory = file.path(project_directory, 'Data')
results_directory = file.path(project_directory, 'Results')
figure_directory = file.path(results_directory, 'Figures')

Source package dependencies and utility functions from ‘Utility Functions.R’ file

source(file.path(scripts_directory, 'Utility Functions.R'))

Load Data

  • load various datafiles
## Vessel Traffic data
vessel_df = 

## Metadata Files
tagging_df = load_tagging_data(file.path(data_directory, 'Molokini_Fish_Tagging_master.xlsx'))
line 1 appears to contain embedded nullsline 2 appears to contain embedded nullsline 3 appears to contain embedded nullsline 4 appears to contain embedded nullsembedded nul(s) found in inputError in names(x) <- value : 
  'names' attribute [2] must be the same length as the vector [1]

Clean Data

  • Associate detections with time of day (day, night, dawn, dusk)
  • Remove detections from tags not associated with this study
  • Remove false detections
unique(molo_df$tag_id)
[1] "47513" "30711" "30754" "51591" "51590" "51593" "39194" "30755" "51594"

Exploratory Data Analysis

Count of individuals tagged by species

tags_by_species = aggregate(tag_id ~ species, data = tagging_df, FUN = uniqueN)
  colnames(tags_by_species) = c('species', 'n_tagged')
print(tags_by_species)

Summary Statistics

Metric Calculations

index of receiver use

Calculate Pianka’s Niche Overlap Index - Pianka (1973) The Structure of Lizard Communities

0 = no overlap, 1 = perfect overlap

Plots

Study Area

molo_basemap = get_map(location = c(lon = -156.496331, lat = 20.633007), zoom = 16, maptype = 'satellite')
Source : https://maps.googleapis.com/maps/api/staticmap?center=20.633007,-156.496331&zoom=16&size=640x640&scale=2&maptype=satellite&language=en-EN&key=xxx-hggZe5I57UhGHb8
molo_basemap = get_map(location = c(lon = -156.496331, lat = 20.633007), zoom = 16, maptype = 'satellite')
Source : https://maps.googleapis.com/maps/api/staticmap?center=20.633007,-156.496331&zoom=16&size=640x640&scale=2&maptype=satellite&language=en-EN&key=xxx-hggZe5I57UhGHb8
receiver_map = ggmap(molo_basemap) + geom_point(data = molo_df[molo_df$receiver != 'Tagging Location', ], mapping = aes(x = lon, y = lat), col = 'red') + labs(x = '°Longitude', y = '°Latitude') + ggsave(filename = 'Receiver Locations Google Map.pdf', path = figure_directory)
Saving 7 x 7 in image
receiver_map = ggmap(molo_basemap) + geom_point(data = molo_df[molo_df$receiver != 'Tagging Location', ], mapping = aes(x = lon, y = lat), col = 'red') + labs(x = '°Longitude', y = '°Latitude') + ggsave(filename = 'Receiver Locations Google Map.pdf', path = figure_directory)
print(receiver_map)

Species Use Plots

## Make species plots for receiver use
for(species in species_receiver_use$species){
  receiver_use_by_spp = ggmap(molo_basemap) + 
    geom_point(data = species_receiver_use[species_receiver_use$species == species, ], 
               mapping = aes(x = lon, y = lat, color = 'red', size =  receiver_use)) + 
    labs(x = '°Longitude', y = '°Latitude') +
    ggsave(filename = paste('Receiver Use by ', species, '.pdf', sep = ''), path = figure_directory)
  print(receiver_use_by_spp)
}
Saving 7 x 7 in image

Day Night Plots

### Day Night Plots
## For all fish
plot_day_night(molo_df, plot_title = 'All Fish')
## By Species
for (spp in unique(molo_df$species)){
  plot_day_night(molo_df[molo_df$tag_id == molo_df$tag_id[molo_df$species == spp], ], plot_title = spp)
}
longer object length is not a multiple of shorter object length

longer object length is not a multiple of shorter object length

longer object length is not a multiple of shorter object length

longer object length is not a multiple of shorter object length

## By Individual
for (tag_id in molo_df$tag_id){
  plot_day_night(molo_df[molo_df$tag_id == tag_id, ], plot_title = paste(tagging_df$species[tagging_df$tag_id == tag_id], '- Tag', as.character(tag_id), sep = ' '))
}

NA

Barplot of detections by date

## Barplot of detections by individual
for(column in colnames(detections_per_day_df)[2:ncol(detections_per_day_df)]){
  ggplot(data = detections_per_day_df, mapping = aes_string(x = 'date', y = column)) +
    geom_bar(stat = "identity") + 
    labs(title = paste('Tag ', strsplit(x = column, split = '_')[[1]][2], sep = ''), x = 'Date', y = 'Detections') + 
    ggsave(filename = paste('Daily Detection Barplot -', column, '.pdf'), path = figure_directory)
}
Saving 7 x 7 in image

Bar plot # of Fish (standardized percent of fish tagged to date) by date and Spp

## Convert detections_per_day to presence/absence
presence_absence_wide_df = detections_per_day_df
for (i in 2:ncol(presence_absence_wide_df)){
  presence_absence_wide_df[ ,i] = as.numeric(presence_absence_wide_df[ ,i] > 0)
}

## Convert from wide to long format
presence_absence_long_df = melt(presence_absence_wide_df, id.vars = c('date'), measure.vars = colnames(presence_absence_wide_df)[2:ncol(presence_absence_wide_df)], variable.name = 'tag_id', value.name = 'detected')

# Drop 'tag_' prefix from tag_id column for matching purposes
presence_absence_long_df$tag_id = levels(presence_absence_long_df$tag_id)[presence_absence_long_df$tag_id]
for(i in 1:nrow(presence_absence_long_df)){
  presence_absence_long_df$tag_id[i] = strsplit(presence_absence_long_df$tag_id[i], split = '_')[[1]][2]
}

## Merge with species from tagging data
presence_absence_long_df = merge(x = presence_absence_long_df, y = tagging_df[ ,c('tag_id', 'species')], on = 'tag_id')

## Drop date and tag pairs preceding the date the fish was tagged
indicies_to_drop = c()
for(i in nrow(presence_absence_long_df)){
  if(as.Date(tagging_df$datetime[tagging_df$tag_id == presence_absence_long_df$tag_id[i]]) <= presence_absence_long_df$date[i]){
    indicies_to_drop = c(indicies_to_drop, i)
  }
}
Error in if (as.Date(tagging_df$datetime[tagging_df$tag_id == presence_absence_long_df$tag_id[i]]) <=  : 
  missing value where TRUE/FALSE needed

Bar plot vessel traffic by date

### LOGIC HERE TO GET TO # BOATS / DAY
## Get max_vessels at any given time, total_vessels


# Make plot for max_vessels 
max_vessels_plot = ggplot(data = vessels_per_day, mapping = aes(x = date, y = max_vessels)) + 
    geom_bar(stat = 'identity') + 
    labs(title = 'Maximum Number of Co-occuring Vessels Daily', x = 'Date', y = '# of Vessels') +
    ggsave(filename = paste('Maximum Number of Co-occuring Vessels Daily.pdf ', species, '.pdf', sep = ''), path = figure_directory)

# Make plot for tptal_vessels 
total_vessels_plot = ggplot(data = vessels_per_day, mapping = aes(x = date, y = total_vessels)) + 
    geom_bar(stat = 'identity') + 
    labs(title = 'Maximum Number of Co-occuring Vessels Daily', x = 'Date', y = '# of Vessels') +
    ggsave(filename = paste('Total Vessels Daily.pdf ', species, '.pdf', sep = ''), path = figure_directory)

print(max_vessels_plot)
print(total_vessels_plot)

Scatter plot x axis boat traffic, y axis presence / absence color by spp

Scatter plot x axis boat traffic, y axis detections per individual color by spp add error bars for daily detections

Residency and dispersal

## Calculate residency
detection_stats$residence_metric = detection_stats$unique_days / detection_stats$days_at_liberty
Error in `$<-.data.frame`(`*tmp*`, residence_metric, value = numeric(0)) : 
  replacement has 0 rows, data has 2

Calculate 30 day moving average of residency, then plot against days since tagging

## Get total days in the study
total_days_in_study = as.numeric(diff.Date(c(min(molo_df$date), max(molo_df$date))))

## Create a dataframe where rows are tag id and columns are study date
present_after_n_days_df = data.frame()

## Determine if a tag was detected on a receiver n days after tagging
for (i in 1:uniqueN(molo_df$tag_id)){
  ## Subset data for individual tags
  indv_data = molo_df[molo_df$tag_id == unique(molo_df$tag_id)[i], ]
  ## Determine if a fish was present n days after tagging
  difftimes = rep(0, len = total_days_in_study)
  # determine difference in days between each unique day a tag was detected and the tag's earliest detection, flip the corresponding value in difftimes array to 1
  detected_dates = unique(indv_data$date)
  for (j in 1:length(detected_dates)){
    difftimes[as.numeric(diff.Date(c(min(indv_data$date), detected_dates[j]))) + 1] = 1
  }
  df_row = c(unique(molo_df$tag_id)[i], difftimes)
  present_after_n_days_df = rbind(present_after_n_days_df, df_row)
}
colnames(present_after_n_days_df) = c('tag_id', as.character(1:total_days_in_study))
Error in names(x) <- value : 
  'names' attribute [3] must be the same length as the vector [2]

Statistical analysis

Calculate mean residency by spp (irregardless of time), then ANOVA by spp Use Tukey’s HSD to determine significance

## ANOVA model for residency metric by species
residence_by_species_anova = aov(residence_metric ~ species, data=detection_stats)
Error in eval(predvars, data, env) : object 'residence_metric' not found

GLM comparing size and residency time by spp independent var (size, time at liberty) dependent (residency index)

## Fit binomial GLM to average residency metric data (proportional between 0-1)
species_glm = glm(residence_metric ~  species + fork_length_cm *days_at_liberty * species, data = detection_stats, family = binomial(logit))
Error in eval(predvars, data, env) : object 'residence_metric' not found

GLM comparing time in crater to vessel traffic

LS0tCnRpdGxlOiAiTW9sb2tpbmkgQW5hbHlzaXMiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCiMgV3JpdHRlbiBieSBTdGV2ZSBTY2hlcnJlciAtIEp1bHkvQXVndXN0IDIwMjEKCiMjIEJhY2tncm91bmQKVGhpcyBub3RlYm9vayBkb2N1bWVudHMgcHJlbGltaW5hcnkgYW5hbHlzaXMgb2YgdHJhY2tpbmcgZGF0YSBmb3IgZmlzaCB0YWdnZWQgaW4gTW9sb2tpbmkgQ3JhdGVyIGJldHdlZW4gMjAyMC0wNS0xNiBhbmQgMjAyMS0wNS0yNC4gCgpUaGUgcHVycG9zZSBvZiB0aGlzIHN0dWR5IGlzIHRvIHVuZGVyc3RhbmQgaG93IGh1bWFuIGltcGFjdHMgYWZmZWN0IHRoZSBmaXNoIG9mIE1vbG9raW5pIENyYXRlcgoKV2UgYXJlIHBhcnRpY3VsYXJseSBpbnRlcmVzdGVkIGluIGFuc3dlcmluZyB0aGUgZm9sbG93aW5nIGh5cG90aGVzZXM6CjEuIElzIHRoZSBwcmVzZW5jZSBvZiBmaXNoIGFmZmVjdGVkIGJ5IHZlc3NlbCBwcmVzZW5jZQoKMi4gRG9lcyB0aGUgcHJvcG9ydGlvbiBvZiB0aW1lIGZpc2ggYXJlIHByZXNlbnQgd2l0aGluIHRoZSBjcmF0ZXIgbmVnYXRpdmVseSBjb3JyZWxhdGVkIHdpdGggdmVzc2VsIHByZXNlbmNlPwoKUHJvcG9zZWQgQXBwcm9hY2g6CjEuIEJlZ2luIGJ5IGNhbGN1bGF0aW5nIHRoZSBudW1iZXIgb2YgZWFjaCBzcGVjaWVzIHRhZ2dlZCBhbmQgYmFzaWMgc3VtbWFyeSBzdGF0aXN0aWNzCjIuIENhbGN1bGF0ZSBNZXRyaWNzCiAgLSBSZWNlaXZlciBVc2UKICAtIFBpYW5rYSdzIE5pY2hlIE92ZXJsYXAKICAtIHJlc2lkZW5jeQozLiBNYWtlIHRoZSBmb2xsb3dpbmcgcGxvdHMKICAtIE1hcCAtIFJlY2VpdmVyIGxvY2F0aW9ucwogIC0gTWFwIC0gQXZlcmFnZSByZWNlaXZlciB1c2UgYnkgU3BlY2llcwogIC0gU2NhdHRlcnBsb3QgLSBkYXkgbmlnaHQgcGxvdHMKICAtIEJhciBQbG90IC0gVGhlIG51bWJlciBvZiBkZXRlY3Rpb25zIHBlciBkYXkgKGluZGl2aWR1YWwpCiAgLSBCYXIgUGxvdCAtIFRoZSBudW1iZXIgb2YgaW5kaXZpZHVhbHMgZGV0ZWN0ZWQgKHNwZWNpZXMpCiAgLSBMaW5lIENoYXJ0IC0gVGhlIHByb3BvcnRpb24gb2YgaW5kaXZpZHVhbHMgZGV0ZWN0ZWQgbiBkYXlzIGFmdGVyIHRhZ2dpbmcgKDMwIGRheSBtb3ZpbmcgYXZlcmFnZSBieSBzcGVjaWVzKQogIC0gQmFyIFBsb3QgLSBEYWlseSB2ZXNzZWwgdHJhZmZpYwogIC0gU2NhdHRlciBQbG90IC0gdmVzc2VsIHRyYWZmaWMgdnMuIHByb3BvcnRpb24gb2YgZmlzaCBkZXRlY3RlZCBpbiBjcmF0ZXIgZGFpbHkgKHNjYXR0ZXJwbG90IGJ5IHNwZWNpZXMpCjQuIFBlcmZvcm0gdGhlIGZvbGxvd2luZyBzdGF0aXN0aWNhbCBUZXN0cwogIC0gQ29tcGFyZSBSZXNpZGVuY3kgUmF0ZXMgYnkgU3BlY2llcwogIC0gQ29tcGFyZSByZXNpZGVuY3kgYnkgc3BlY2llcywgc2l6ZSwgYW5kIHRpbWUgYXQgbGliZXJ0eQogIC0gQ3JlYXRlIGEgR0xNIGNvbXBhcmluZyAjIG9mIGluZGl2aWR1YWxzIGluIGNyYXRlciByZWdyZXNzZWQgYWdhaW5zdCBib2F0IHRyYWZmaWMgYW5kIHNwZWNpZXMgdXNpbmcgQVIoMSkgdGVybSBvbiBkZXBlbmRlbnQgdmFyaWFibGUgb24gc29tZSB0aW1lIHNjYWxlIChkYWlseT8gNiBob3Vycz8gZGVwZW5kcyBvbiByZXNvbHV0aW9uIG9mIHZlc3NlbCBkYXRhKQoKIyBXb3Jrc3BhY2UgU2V0dXAKIyMgRXN0YWJsaXNoIERpcmVjdG9yeSBIZWlyYXJjaHkKYGBge3J9CnByb2plY3RfZGlyZWN0b3J5ID0gJy9Vc2Vycy9zdGVwaGVuc2NoZXJyZXIvRG9jdW1lbnRzL1Byb2dyYW1taW5nL1Byb2plY3RzL01vbG9raW5pJwpzY3JpcHRzX2RpcmVjdG9yeSA9IGZpbGUucGF0aChwcm9qZWN0X2RpcmVjdG9yeSwgJ0FuYWx5c2lzIFNjcmlwdHMnKQpkYXRhX2RpcmVjdG9yeSA9IGZpbGUucGF0aChwcm9qZWN0X2RpcmVjdG9yeSwgJ0RhdGEnKQpyZXN1bHRzX2RpcmVjdG9yeSA9IGZpbGUucGF0aChwcm9qZWN0X2RpcmVjdG9yeSwgJ1Jlc3VsdHMnKQpmaWd1cmVfZGlyZWN0b3J5ID0gZmlsZS5wYXRoKHJlc3VsdHNfZGlyZWN0b3J5LCAnRmlndXJlcycpCgpgYGAKCiMjIFNvdXJjZSBwYWNrYWdlIGRlcGVuZGVuY2llcyBhbmQgdXRpbGl0eSBmdW5jdGlvbnMgZnJvbSAnVXRpbGl0eSBGdW5jdGlvbnMuUicgZmlsZQpgYGB7cn0Kc291cmNlKGZpbGUucGF0aChzY3JpcHRzX2RpcmVjdG9yeSwgJ1V0aWxpdHkgRnVuY3Rpb25zLlInKSkKYGBgCgojIyBMb2FkIERhdGEKLSBsb2FkIHZhcmlvdXMgZGF0YWZpbGVzIApgYGB7cn0KIyMgRmlsZXMgZnJvbSBWVUUgCm1vbG9fZGYgPSBsb2FkX3ZlbWNvX2RhdGEoZmlsZS5wYXRoKGRhdGFfZGlyZWN0b3J5LCAnVlVFX0V4cG9ydC5jc3YnKSkKZmFsc2VfZGV0ZWN0aW9uc19kZiA9IGxvYWRfZmRmX3JlcG9ydChmaWxlLnBhdGgoZGF0YV9kaXJlY3RvcnksICdGREEuY3N2JykpCgojIyBWZXNzZWwgVHJhZmZpYyBkYXRhCnZlc3NlbF9kZiA9IAoKIyMgTWV0YWRhdGEgRmlsZXMKdGFnZ2luZ19kZiA9IGxvYWRfdGFnZ2luZ19kYXRhKGZpbGUucGF0aChkYXRhX2RpcmVjdG9yeSwgJ01vbG9raW5pX0Zpc2hfVGFnZ2luZ19tYXN0ZXIueGxzeCcpKQoKcmVjZWl2ZXJfZGYgPSBsb2FkX3JlY2VpdmVyX2RhdGEoZmlsZS5wYXRoKGRhdGFfZGlyZWN0b3J5LCApKQpgYGAKCiMjIENsZWFuIERhdGEKLSBBc3NvY2lhdGUgZGV0ZWN0aW9ucyB3aXRoIHRpbWUgb2YgZGF5IChkYXksIG5pZ2h0LCBkYXduLCBkdXNrKQotIFJlbW92ZSBkZXRlY3Rpb25zIGZyb20gdGFncyBub3QgYXNzb2NpYXRlZCB3aXRoIHRoaXMgc3R1ZHkKLSBSZW1vdmUgZmFsc2UgZGV0ZWN0aW9ucwpgYGB7cn0KIyMgQXNzb2NpYXRlIGRldGVjdGlvbnMgd2l0aCB0aW1lIG9mIGRheQptb2xvX2RmID0gZ2V0X3RpbWVfb2ZfZGF5KG1vbG9fZGYpCnVuaXF1ZShtb2xvX2RmJHRhZ19pZCkKCiMjIFJlbW92ZSBpcnJlbGV2YW50IHRhZ3MKbW9sb19kZiA9IG1vbG9fZGZbbW9sb19kZiR0YWdfaWQgJWluJSB0YWdnaW5nX2RmJHRhZ19pZCwgXQp1bmlxdWUobW9sb19kZiR0YWdfaWQpCgojIyBDb21iaW5lIHZ1ZSBkZiB3aXRoIHRhZ2dpbmcgZGYKbW9sb19kZiA9IGpvaW4oeCA9IG1vbG9fZGYsIHkgPSB0YWdnaW5nX2RmWyAsYygndGFnX2lkJywgJ3NwZWNpZXMnLCAnZm9ya19sZW5ndGgnLCAnZGF0ZScgKV0sIGJ5ID0gJ3RhZ19pZCcsIHR5cGUgPSAnbGVmdCcpCnVuaXF1ZShtb2xvX2RmJHRhZ19pZCkKCiMjIEZpbHRlciBmYWxzZSBkZXRlY3Rpb25zCiMgbW9sb19kZiA9IGZpbHRlcl9mYWxzZV9kZXRlY3Rpb25zKG1vbG9fZGYpCmBgYAoKIyBFeHBsb3JhdG9yeSBEYXRhIEFuYWx5c2lzCiMjIENvdW50IG9mIGluZGl2aWR1YWxzIHRhZ2dlZCBieSBzcGVjaWVzCmBgYHtyfQp0YWdzX2J5X3NwZWNpZXMgPSBhZ2dyZWdhdGUodGFnX2lkIH4gc3BlY2llcywgZGF0YSA9IHRhZ2dpbmdfZGYsIEZVTiA9IHVuaXF1ZU4pCiAgY29sbmFtZXModGFnc19ieV9zcGVjaWVzKSA9IGMoJ3NwZWNpZXMnLCAnbl90YWdnZWQnKQpwcmludCh0YWdzX2J5X3NwZWNpZXMpCmBgYAoKIyMgU3VtbWFyeSBTdGF0aXN0aWNzCmBgYHtyfQojIFRpbWUgYXQgbGliZXJ0eQp0aW1lX2F0X2xpYmVydHkgPSBjYWxjdWxhdGVfdGltZV9hdF9saWJlcnR5KG1vbG9fZGYpCgojIERheXMgRGV0ZWN0ZWQKZGF5c19kZXRlY3RlZCA9IGNhbGN1bGF0ZV9kYXlzX2RldGVjdGVkKG1vbG9fZGYpCgojICUgb2YgZGF5cyBkZXRlY3RlZApkZXRlY3Rpb25fc3RhdHMgPSBtZXJnZSh4ID0gZGF5c19kZXRlY3RlZCwgeSA9IHRpbWVfYXRfbGliZXJ0eVsgLGMoJ3RhZ19pZCcsICdkYXlzX2F0X2xpYmVydHknKV0sIG9uLnggPSAndGFnX2lkJywgb24ueSA9ICd0YWdfaWQnKQpkZXRlY3Rpb25fc3RhdHMkcGVyY2VudF9kYXlzX2RldGVjdGVkID0gcm91bmQoZGV0ZWN0aW9uX3N0YXRzJHVuaXF1ZV9kYXlzIC8gZGV0ZWN0aW9uX3N0YXRzJGRheXNfYXRfbGliZXJ0eSwgNCkgKiAxMDAKCiMgTWVyZ2Ugd2l0aCB0YWdnaW5nIGRhdGEgdG8gZ2V0IGZpc2ggaW5mbwpkZXRlY3Rpb25fc3RhdHMgPSBtZXJnZSh4ID0gdGFnZ2luZ19kZlsgLGMoJ2RhdGUnLCAnc3BlY2llcycsICd0YWdfaWQnLCAnZm9ya19sZW5ndGgnKV0sIHkgPSBkZXRlY3Rpb25fc3RhdHMsIG9uLnggPSAndGFnX2lkJywgb24ueSA9ICd0YWdfaWQnKQpkZXRlY3Rpb25fc3RhdHMgPSBkZXRlY3Rpb25fc3RhdHNbb3JkZXIoZGV0ZWN0aW9uX3N0YXRzJHNwZWNpZXMsIGRldGVjdGlvbl9zdGF0cyRkYXRlLCBkZXRlY3Rpb25fc3RhdHMkdGFnX2lkKSwgXQpwcmludChkZXRlY3Rpb25fc3RhdHMpCmBgYAoKIyBNZXRyaWMgQ2FsY3VsYXRpb25zCiMjIGluZGV4IG9mIHJlY2VpdmVyIHVzZQpgYGB7cn0KIyMgc3VtIGFsbCBzcHAsIHN1bSBhbGwgaW5kaXZpZHVhbHMgKGRldGVjdGlvbnMgb2YgdGFnIGF0IGdpdmVuIHJlY2lldmVyIC8gYWxsIGRldGVjdGlvbnMgb2YgdGFnKQoKIyMgQ2FsY3VsYXRlIHVuaXF1ZSBkZXRlY3Rpb25zIHBlciB0YWcgcGVyIHJlY2VpdmVyIHN0YXRpb24KZGV0ZWN0aW9uc19wZXJfdGFnX3Blcl9yZWNlaXZlciA9IGFnZ3JlZ2F0ZShkYXRldGltZX50YWdfaWQrcmVjZWl2ZXIrc3BlY2llcywgZGF0YSA9IG1vbG9fZGYsIEZVTiA9IHVuaXF1ZU4pCmNvbG5hbWVzKGRldGVjdGlvbnNfcGVyX3RhZ19wZXJfcmVjZWl2ZXIpID0gYygndGFnX2lkJywgJ3JlY2VpdmVyJywgJ3NwZWNpZXMnLCAnZGV0ZWN0aW9ucycpCgojIyBDYWxjdWxhdGUgcmVjZWl2ZXIgdXNlIG1ldHJpYyBmb3IgZWFjaCBmaXNoIGFuZCByZWNlaXZlciBwYWlyCmRldGVjdGlvbnNfcGVyX3RhZ19wZXJfcmVjZWl2ZXIkcmVjZWl2ZXJfdXNlID0gMApmb3IgKHNwZWNpZXMgaW4gZGV0ZWN0aW9uc19wZXJfdGFnX3Blcl9yZWNlaXZlciRzcGVjaWVzKXsKICBmb3IgKGkgaW4gMTpucm93KGRldGVjdGlvbnNfcGVyX3RhZ19wZXJfcmVjZWl2ZXIpKXsKICAgIGRldGVjdGlvbnNfcGVyX3RhZ19wZXJfcmVjZWl2ZXIkcmVjZWl2ZXJfdXNlW2ldID0gZGV0ZWN0aW9uc19wZXJfdGFnX3Blcl9yZWNlaXZlciRkZXRlY3Rpb25zW2ldIC8gc3VtKGRldGVjdGlvbnNfcGVyX3RhZ19wZXJfcmVjZWl2ZXIkZGV0ZWN0aW9uc1tkZXRlY3Rpb25zX3Blcl90YWdfcGVyX3JlY2VpdmVyJHRhZ19pZCA9PSBkZXRlY3Rpb25zX3Blcl90YWdfcGVyX3JlY2VpdmVyJHRhZ19pZFtpXV0pCiAgfQp9CgojIyBDYWxjdWxhdGUgYXZlcmFnZSByZWNlaXZlciB1c2UgbWV0cmljIGZvciBlYWNoIHRhZyAtIE9taXQgc3RhdGlvbnMgd2l0aCBubyB1c2UgYXMgdGhpcyB3b3VsZCBiaWFzIG1ldHJpYwppbmR2aWR1YWxfcmVjZWl2ZXJfdXNlID0gYWdncmVnYXRlKHJlY2VpdmVyX3VzZX50YWdfaWQrc3BlY2llcywgZGF0YSA9IGRldGVjdGlvbnNfcGVyX3RhZ19wZXJfcmVjZWl2ZXJbZGV0ZWN0aW9uc19wZXJfdGFnX3Blcl9yZWNlaXZlciRyZWNlaXZlcl91c2UgPiAwLCBdLCBGVU4gPSBtZWFuKQoKIyMgQWRkIHRoaXMgaW5mb3JtYXRpb24gdG8gZGV0ZWN0aW9uX3N0YXRzCmRldGVjdGlvbl9zdGF0cyA9IG1lcmdlKGRldGVjdGlvbl9zdGF0cywgaW5kdmlkdWFsX3JlY2VpdmVyX3VzZSwgb24gPSAndGFnX2lkJykKCiMjIENhbGN1bGF0ZSByZWNlaXZlciB1c2UgbWV0cmljIGJ5IHNwZWNpZXMKc3BlY2llc19yZWNlaXZlcl91c2UgPSBhZ2dyZWdhdGUocmVjZWl2ZXJfdXNlfnNwZWNpZXMsIGRhdGEgPSBpbmR2aWR1YWxfcmVjZWl2ZXJfdXNlLCBGVU4gPSBtZWFuKQpjb2xuYW1lcyhzcGVjaWVzX3JlY2VpdmVyX3VzZSkgPSBjKCdzcGVjaWVzJywgJ3JlY2VpdmVyX3VzZScpCgpwcmludChzcGVjaWVzX3JlY2VpdmVyX3VzZSkKYGBgIAojIyBDYWxjdWxhdGUgUGlhbmthJ3MgTmljaGUgT3ZlcmxhcCBJbmRleCAtIFBpYW5rYSAoMTk3MykgVGhlIFN0cnVjdHVyZSBvZiBMaXphcmQgQ29tbXVuaXRpZXMKIDAgPSBubyBvdmVybGFwLCAxID0gcGVyZmVjdCBvdmVybGFwCmBgYHtyfQojIyBHZXQgc3BlY2llcyBhc3NvY2lhdGVkIHdpdGggZWFjaCB0YWcgaW4gZGV0ZWN0aW9uc19wZXJfdGFnX3Blcl9yZWNlaXZlcgpkZXRlY3Rpb25zX3Blcl90YWdfcGVyX3JlY2VpdmVyID0gbWVyZ2UoeCA9IGRldGVjdGlvbnNfcGVyX3RhZ19wZXJfcmVjZWl2ZXIsIHkgPSB0YWdnaW5nX2RmWyAsYygndGFnX2lkJywgJ3NwZWNpZXMnKV0sIG9uID0gJ3RhZ19pZCcpCgojIyBBZ2dyZWdhdGUgZGF0YSBhdmVyYWdlZCBieSBzcGVjaWVzCnJlY2VpdmVyX3VzZV9hZ2dyZWdhdGVkX2J5X3NwZWNpZXMgPSBhZ2dyZWdhdGUocmVjZWl2ZXJfdXNlIH4gc3BlY2llcyArIHJlY2VpdmVyICwgZGF0YSA9IGRldGVjdGlvbnNfcGVyX3RhZ19wZXJfcmVjZWl2ZXIsIEZVTiA9IG1lYW4pCiAgY29sbmFtZXMocmVjZWl2ZXJfdXNlX2FnZ3JlZ2F0ZWRfYnlfc3BlY2llcykgPSBjKCdzcGVjaWVzJywgJ3JlY2VpdmVyJywgJ2F2Z191c2VfaW5kZXgnKQogIAojIyBSZXNoYXBlIGZyb20gTG9uZyB0byBXaWRlIGZvcm1hdApyZWNlaXZlcl91c2VfYWdncmVnYXRlZF9ieV9zcGVjaWVzX3dpZGUgPSBkY2FzdChyZWNlaXZlcl91c2VfYWdncmVnYXRlZF9ieV9zcGVjaWVzLCBzcGVjaWVzIH4gcmVjZWl2ZXIpCgojIyBHZXQgYWxsIHNwZWNpZXMgY29tYmluYXRpb25zIApzcGVjaWVzX2NvbWJvcyA9IGRhdGEuZnJhbWUoJ3NwZWNpZXNfMScgPSAnYScsICdzcGVjaWVzXzInID0gJ2EnKQpmb3IgKGkgaW4gMTpucm93KHJlY2VpdmVyX3VzZV9hZ2dyZWdhdGVkX2J5X3NwZWNpZXNfd2lkZSkpewogICAgaWYoaSAhPSBucm93KHJlY2VpdmVyX3VzZV9hZ2dyZWdhdGVkX2J5X3NwZWNpZXNfd2lkZSkpewogICAgZm9yIChqIGluIChpKzEpOm5yb3cocmVjZWl2ZXJfdXNlX2FnZ3JlZ2F0ZWRfYnlfc3BlY2llc193aWRlKSl7CiAgICAgIHNwZWNpZXNfY29tYm9zID0gcmJpbmQoc3BlY2llc19jb21ib3MsIGRhdGEuZnJhbWUoJ3NwZWNpZXNfMScgPSByZWNlaXZlcl91c2VfYWdncmVnYXRlZF9ieV9zcGVjaWVzX3dpZGUkc3BlY2llc1tpXSwgJ3NwZWNpZXNfMicgPSByZWNlaXZlcl91c2VfYWdncmVnYXRlZF9ieV9zcGVjaWVzX3dpZGUkc3BlY2llc1tqXSkpCiAgICAgIHByaW50KGopCiAgICB9CiAgfQp9CgojIyBDYWxjdWxhdGUgUGlhbmthJ3MgaW5kZXggZm9yIGFsbCBwYWlycwpzcGVjaWVzX2NvbWJvcyRwaWFua2FfaW5kZXggPSAwCmZvcihpIGluIDE6bnJvdyhzcGVjaWVzX2NvbWJvcykpewogIHNwZWNpZXNfY29tYm9zJHBpYW5rYV9pbmRleFtpXSA9IHN1bShyZWNlaXZlcl91c2VfYWdncmVnYXRlZF9ieV9zcGVjaWVzX3dpZGVbcmVjZWl2ZXJfdXNlX2FnZ3JlZ2F0ZWRfYnlfc3BlY2llc193aWRlJHNwZWNpZXMgPT0gc3BlY2llc19jb21ib3Mkc3BlY2llc18xW2ldLCAtMV0gKiAKICAgICByZWNlaXZlcl91c2VfYWdncmVnYXRlZF9ieV9zcGVjaWVzX3dpZGVbcmVjZWl2ZXJfdXNlX2FnZ3JlZ2F0ZWRfYnlfc3BlY2llc193aWRlJHNwZWNpZXMgPT0gc3BlY2llc19jb21ib3Mkc3BlY2llc18yW2ldLCAtMV0pIC8KICAgIChzcXJ0KHN1bShyZWNlaXZlcl91c2VfYWdncmVnYXRlZF9ieV9zcGVjaWVzX3dpZGVbcmVjZWl2ZXJfdXNlX2FnZ3JlZ2F0ZWRfYnlfc3BlY2llc193aWRlJHNwZWNpZXMgPT0gc3BlY2llc19jb21ib3Mkc3BlY2llc18xW2ldLCAtMV0gXiAyKSAqIAogICAgc3VtKHJlY2VpdmVyX3VzZV9hZ2dyZWdhdGVkX2J5X3NwZWNpZXNfd2lkZVtyZWNlaXZlcl91c2VfYWdncmVnYXRlZF9ieV9zcGVjaWVzX3dpZGUkc3BlY2llcyA9PSBzcGVjaWVzX2NvbWJvcyRzcGVjaWVzXzJbaV0sIC0xXSBeIDIpKSkKfQoKcHJpbnQoc3BlY2llc19jb21ib3MpCmBgYAoKIyMgUGxvdHMKU3R1ZHkgQXJlYQpgYGB7cn0KIyMgUGxvdCBzdHVkeSBhcmVhIGFuZCByZWNlaXZlcnMKbGlicmFyeShnZ21hcCkKCm1vbG9fYmFzZW1hcCA9IGdldF9tYXAobG9jYXRpb24gPSBjKGxvbiA9IC0xNTYuNDk2MzMxLCBsYXQgPSAyMC42MzMwMDcpLCB6b29tID0gMTYsIG1hcHR5cGUgPSAnc2F0ZWxsaXRlJykKcmVjZWl2ZXJfbWFwID0gZ2dtYXAobW9sb19iYXNlbWFwKSArIGdlb21fcG9pbnQoZGF0YSA9IG1vbG9fZGZbbW9sb19kZiRyZWNlaXZlciAhPSAnVGFnZ2luZyBMb2NhdGlvbicsIF0sIG1hcHBpbmcgPSBhZXMoeCA9IGxvbiwgeSA9IGxhdCksIGNvbCA9ICdyZWQnKSArIGxhYnMoeCA9ICfCsExvbmdpdHVkZScsIHkgPSAnwrBMYXRpdHVkZScpICsgZ2dzYXZlKGZpbGVuYW1lID0gJ1JlY2VpdmVyIExvY2F0aW9ucyBHb29nbGUgTWFwLnBkZicsIHBhdGggPSBmaWd1cmVfZGlyZWN0b3J5KQpwcmludChyZWNlaXZlcl9tYXApCmBgYAoKIyMjIFNwZWNpZXMgVXNlIFBsb3RzCmBgYHtyfQojIyBDYWxjdWxhdGUgbWVyZ2UgZGV0ZWN0aW9ucyBvZiB0YWcgcGVyIHJlY2VpdmVyIHdpdGggc3BlY2llcyBpbmZvcm1hdGlvbgpkZXRlY3Rpb25zX3Blcl90YWdfcGVyX3JlY2VpdmVyID0gbWVyZ2UoeCA9IGRldGVjdGlvbnNfcGVyX3RhZ19wZXJfcmVjZWl2ZXIsIHkgPSB0YWdnaW5nX2RmWyAsYygndGFnX2lkJywgJ3NwZWNpZXMnKV0sIG9uID0gJ3RhZ19pZCcpCgojIyBHZXQgYXZlcmFnZSB1c2Ugb2YgcmVjZWl2ZXIgYnkgc3BlY2llcyAKc3BlY2llc19yZWNlaXZlcl91c2UgPSBhZ2dyZWdhdGUocmVjZWl2ZXJfdXNlfnNwZWNpZXMrcmVjZWl2ZXIsIGRhdGEgPSBkZXRlY3Rpb25zX3Blcl90YWdfcGVyX3JlY2VpdmVyLCBGVU4gPSBtZWFuKQogIGNvbG5hbWVzKHNwZWNpZXNfcmVjZWl2ZXJfdXNlKSA9IGMoJ3NwZWNpZXMnLCAncmVjZWl2ZXInICwgJ3JlY2VpdmVyX3VzZScpCiAgCiMjIE1lcmdlIHdpdGggbGF0IGxvbiBwb3NpdGlvbnMgZm9yIGVhY2ggcmVjZWl2ZXIgZnJvbSBtb2xvX2RmCnJlY2VpdmVyX3Bvc3Rpb25zID0gIHVuaXF1ZShtb2xvX2RmWyAsYygncmVjZWl2ZXInLCAnbGF0JywgJ2xvbicpXSkKc3BlY2llc19yZWNlaXZlcl91c2UgPSBtZXJnZSh4ID0gc3BlY2llc19yZWNlaXZlcl91c2UsIHkgPSByZWNlaXZlcl9wb3N0aW9ucywgb24gPSAncmVjZWl2ZXInLCBhbGwueCA9IFQsIGFsbC55ID0gRikKCiMjIE1ha2Ugc3BlY2llcyBwbG90cyBmb3IgcmVjZWl2ZXIgdXNlCmZvcihzcGVjaWVzIGluIHNwZWNpZXNfcmVjZWl2ZXJfdXNlJHNwZWNpZXMpewogIHJlY2VpdmVyX3VzZV9ieV9zcHAgPSBnZ21hcChtb2xvX2Jhc2VtYXApICsgCiAgICBnZW9tX3BvaW50KGRhdGEgPSBzcGVjaWVzX3JlY2VpdmVyX3VzZVtzcGVjaWVzX3JlY2VpdmVyX3VzZSRzcGVjaWVzID09IHNwZWNpZXMsIF0sIAogICAgICAgICAgICAgICBtYXBwaW5nID0gYWVzKHggPSBsb24sIHkgPSBsYXQsIGNvbG9yID0gJ3JlZCcsIHNpemUgPSAgcmVjZWl2ZXJfdXNlKSkgKyAKICAgIGxhYnMoeCA9ICfCsExvbmdpdHVkZScsIHkgPSAnwrBMYXRpdHVkZScpICsKICAgIGdnc2F2ZShmaWxlbmFtZSA9IHBhc3RlKCdSZWNlaXZlciBVc2UgYnkgJywgc3BlY2llcywgJy5wZGYnLCBzZXAgPSAnJyksIHBhdGggPSBmaWd1cmVfZGlyZWN0b3J5KQogIHByaW50KHJlY2VpdmVyX3VzZV9ieV9zcHApCn0KYGBgCgojIyMgRGF5IE5pZ2h0IFBsb3RzCmBgYHtyfQojIyMgRGF5IE5pZ2h0IFBsb3RzCiMjIEZvciBhbGwgZmlzaApwbG90X2RheV9uaWdodChtb2xvX2RmLCBwbG90X3RpdGxlID0gJ0FsbCBGaXNoJykKCiMjIEJ5IFNwZWNpZXMKZm9yIChzcHAgaW4gdW5pcXVlKG1vbG9fZGYkc3BlY2llcykpewogIHBsb3RfZGF5X25pZ2h0KG1vbG9fZGZbbW9sb19kZiR0YWdfaWQgPT0gbW9sb19kZiR0YWdfaWRbbW9sb19kZiRzcGVjaWVzID09IHNwcF0sIF0sIHBsb3RfdGl0bGUgPSBzcHApCn0KCiMjIEJ5IEluZGl2aWR1YWwKZm9yICh0YWdfaWQgaW4gbW9sb19kZiR0YWdfaWQpewogIHBsb3RfZGF5X25pZ2h0KG1vbG9fZGZbbW9sb19kZiR0YWdfaWQgPT0gdGFnX2lkLCBdLCBwbG90X3RpdGxlID0gcGFzdGUodGFnZ2luZ19kZiRzcGVjaWVzW3RhZ2dpbmdfZGYkdGFnX2lkID09IHRhZ19pZF0sICctIFRhZycsIGFzLmNoYXJhY3Rlcih0YWdfaWQpLCBzZXAgPSAnICcpKQp9CmBgYAoKCiMjIyBCYXJwbG90IG9mIGRldGVjdGlvbnMgYnkgZGF0ZQpgYGB7cn0KIyMjIEJhciBwbG90IG9mIGRldGVjdGlvbnMgaW4gY3JhdGVyIGJ5IGRhdGUgCmRldGVjdGlvbnNfcGVyX2RheV9kZiA9IGNvdW50X2RldGVjdGlvbnNfcGVyX2RhdGUobW9sb19kZikKCiMjIEJhcnBsb3Qgb2YgZGV0ZWN0aW9ucyBieSBpbmRpdmlkdWFsCmZvcihjb2x1bW4gaW4gY29sbmFtZXMoZGV0ZWN0aW9uc19wZXJfZGF5X2RmKVsyOm5jb2woZGV0ZWN0aW9uc19wZXJfZGF5X2RmKV0pewogIGdncGxvdChkYXRhID0gZGV0ZWN0aW9uc19wZXJfZGF5X2RmLCBtYXBwaW5nID0gYWVzX3N0cmluZyh4ID0gJ2RhdGUnLCB5ID0gY29sdW1uKSkgKwogICAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIpICsgCiAgICBsYWJzKHRpdGxlID0gcGFzdGUoJ1RhZyAnLCBzdHJzcGxpdCh4ID0gY29sdW1uLCBzcGxpdCA9ICdfJylbWzFdXVsyXSwgc2VwID0gJycpLCB4ID0gJ0RhdGUnLCB5ID0gJ0RldGVjdGlvbnMnKSArIAogICAgZ2dzYXZlKGZpbGVuYW1lID0gcGFzdGUoJ0RhaWx5IERldGVjdGlvbiBCYXJwbG90IC0nLCBjb2x1bW4sICcucGRmJyksIHBhdGggPSBmaWd1cmVfZGlyZWN0b3J5KQp9CgoKIyMgRGV0ZWN0aW9ucyBieSBzcGVjaWVzCgojIyBCYXJwbG90IG9mIGFsbCBkZXRlY3Rpb25zCmFnZ3JlZ2F0ZShkYXRhID0KYGBgCgojIyMgQmFyIHBsb3QgIyBvZiBGaXNoIChzdGFuZGFyZGl6ZWQgcGVyY2VudCBvZiBmaXNoIHRhZ2dlZCB0byBkYXRlKSBieSBkYXRlIGFuZCBTcHAKYGBge3J9CiMjIENvbnZlcnQgZGV0ZWN0aW9uc19wZXJfZGF5IHRvIHByZXNlbmNlL2Fic2VuY2UKcHJlc2VuY2VfYWJzZW5jZV93aWRlX2RmID0gZGV0ZWN0aW9uc19wZXJfZGF5X2RmCmZvciAoaSBpbiAyOm5jb2wocHJlc2VuY2VfYWJzZW5jZV93aWRlX2RmKSl7CiAgcHJlc2VuY2VfYWJzZW5jZV93aWRlX2RmWyAsaV0gPSBhcy5udW1lcmljKHByZXNlbmNlX2Fic2VuY2Vfd2lkZV9kZlsgLGldID4gMCkKfQoKIyMgQ29udmVydCBmcm9tIHdpZGUgdG8gbG9uZyBmb3JtYXQKcHJlc2VuY2VfYWJzZW5jZV9sb25nX2RmID0gbWVsdChwcmVzZW5jZV9hYnNlbmNlX3dpZGVfZGYsIGlkLnZhcnMgPSBjKCdkYXRlJyksIG1lYXN1cmUudmFycyA9IGNvbG5hbWVzKHByZXNlbmNlX2Fic2VuY2Vfd2lkZV9kZilbMjpuY29sKHByZXNlbmNlX2Fic2VuY2Vfd2lkZV9kZildLCB2YXJpYWJsZS5uYW1lID0gJ3RhZ19pZCcsIHZhbHVlLm5hbWUgPSAnZGV0ZWN0ZWQnKQoKIyBEcm9wICd0YWdfJyBwcmVmaXggZnJvbSB0YWdfaWQgY29sdW1uIGZvciBtYXRjaGluZyBwdXJwb3NlcwpwcmVzZW5jZV9hYnNlbmNlX2xvbmdfZGYkdGFnX2lkID0gbGV2ZWxzKHByZXNlbmNlX2Fic2VuY2VfbG9uZ19kZiR0YWdfaWQpW3ByZXNlbmNlX2Fic2VuY2VfbG9uZ19kZiR0YWdfaWRdCmZvcihpIGluIDE6bnJvdyhwcmVzZW5jZV9hYnNlbmNlX2xvbmdfZGYpKXsKICBwcmVzZW5jZV9hYnNlbmNlX2xvbmdfZGYkdGFnX2lkW2ldID0gc3Ryc3BsaXQocHJlc2VuY2VfYWJzZW5jZV9sb25nX2RmJHRhZ19pZFtpXSwgc3BsaXQgPSAnXycpW1sxXV1bMl0KfQoKIyMgTWVyZ2Ugd2l0aCBzcGVjaWVzIGZyb20gdGFnZ2luZyBkYXRhCnByZXNlbmNlX2Fic2VuY2VfbG9uZ19kZiA9IG1lcmdlKHggPSBwcmVzZW5jZV9hYnNlbmNlX2xvbmdfZGYsIHkgPSB0YWdnaW5nX2RmWyAsYygndGFnX2lkJywgJ3NwZWNpZXMnKV0sIG9uID0gJ3RhZ19pZCcpCgojIyBEcm9wIGRhdGUgYW5kIHRhZyBwYWlycyBwcmVjZWRpbmcgdGhlIGRhdGUgdGhlIGZpc2ggd2FzIHRhZ2dlZAppbmRpY2llc190b19kcm9wID0gYygpCmZvcihpIGluIG5yb3cocHJlc2VuY2VfYWJzZW5jZV9sb25nX2RmKSl7CiAgaWYoYXMuRGF0ZSh0YWdnaW5nX2RmJGRhdGV0aW1lW3RhZ2dpbmdfZGYkdGFnX2lkID09IHByZXNlbmNlX2Fic2VuY2VfbG9uZ19kZiR0YWdfaWRbaV1dKSA8PSBwcmVzZW5jZV9hYnNlbmNlX2xvbmdfZGYkZGF0ZVtpXSl7CiAgICBpbmRpY2llc190b19kcm9wID0gYyhpbmRpY2llc190b19kcm9wLCBpKQogIH0KfQpwcmVzZW5jZV9hYnNlbmNlX2xvbmdfZGYgPSBwcmVzZW5jZV9hYnNlbmNlX2xvbmdfZGZbLWluZGljaWVzX3RvX2Ryb3AsIF0KCiMjIEdldCBhIGxpc3Qgb2YgYWN0aXZlIHRhZ3MgYnkgZGF0ZSBhbmQgc3BlY2llcwphY3RpdmVfdGFnc19ieV9kYXRlID0gYWdncmVnYXRlKHRhZ19pZCB+IGRhdGUgKyBzcGVjaWVzLCBkYXRhID0gcHJlc2VuY2VfYWJzZW5jZV9sb25nX2RmLCBGVU4gPSB1bmlxdWVOKQogIGNvbG5hbWVzKGFjdGl2ZV90YWdzX2J5X2RhdGUpID0gYygnZGF0ZScsICdzcGVjaWVzJywgJ2RlcGxveWVkX3RhZ3MnKQoKIyMgU3RhbmRhcmRpemUgdGFnIGNvdW50cyBieSB0YWdzIGRlcGxveWVkIGFuZCBwbG90IGFzICUgb2YgdGFncyBkZXRlY3RlZCBwZXIgZGF5IGJ5IHNwZWNpZXMKZm9yKHNwZWNpZXMgaW4gdW5pcXVlKHByZXNlbmNlX2Fic2VuY2VfbG9uZ19kZiRzcGVjaWVzKSl7CiAgCiAgIyBDb3VudCBudW1iZXIgb2YgdGFncyBkZXRlY3RlZCBkYWlseSBieSBzcGVjaWVzIAogIHByZXNlbmNlX2Fic2VuY2VfYnlfc3BwX2RmID0gYWdncmVnYXRlKGRldGVjdGVkfmRhdGUsIGRhdGEgPSBwcmVzZW5jZV9hYnNlbmNlX2xvbmdfZGZbcHJlc2VuY2VfYWJzZW5jZV9sb25nX2RmJHNwZWNpZXMgPT0gc3BlY2llcywgXSwgRlVOID0gc3VtKQogIGNvbG5hbWVzKHByZXNlbmNlX2Fic2VuY2VfYnlfc3BwX2RmKSA9IGMoJ2RhdGUnLCAndGFnc19kZXRlY3RlZCcpCiAgCiAgIyBTdGFuZGFyZGl6ZSBkYWlseSB0YWcgY291bnQgYnkgdGhlIG51bWJlciBvZiB0YWdzIGRlcGxveWVkCiAgcHJlc2VuY2VfYWJzZW5jZV9ieV9zcHBfZGYgPSBtZXJnZSh4ID0gcHJlc2VuY2VfYWJzZW5jZV9ieV9zcHBfZGYsIHkgPSBhY3RpdmVfdGFnc19ieV9kYXRlW2FjdGl2ZV90YWdzX2J5X2RhdGUkc3BlY2llcyA9PSBzcGVjaWVzLCBdLCBvbiA9ICdkYXRlJykKICBwcmVzZW5jZV9hYnNlbmNlX2J5X3NwcF9kZiRwZXJjZW50X3RhZ3NfZGV0ZWN0ZWQgPSBwcmVzZW5jZV9hYnNlbmNlX2J5X3NwcF9kZiRkZXRlY3RlZCAvIHByZXNlbmNlX2Fic2VuY2VfYnlfc3BwX2RmJGRlcGxveWVkX3RhZ3MKICAKICAjIE1ha2UgcGxvdCBhdCBzcGVjaWVzIGxldmVsCiAgZ2dwbG90KGRhdGEgPSBwcmVzZW5jZV9hYnNlbmNlX2J5X3NwcF9kZiwgbWFwcGluZyA9IGFlcyh4ID0gZGF0ZSwgeSA9IHBlcmNlbnRfdGFnc19kZXRlY3RlZCkpICsgCiAgICBnZW9tX2JhcihzdGF0ID0gJ2lkZW50aXR5JykgKyAKICAgIGxhYnModGl0bGUgPSBzcGVjaWVzLCB4ID0gJ0RhdGUnLCB5ID0gJyUgb2YgdGFncyBkZXRlY3RlZCcpICsKICAgIGdnc2F2ZShmaWxlbmFtZSA9IHBhc3RlKCdEZXRlY3Rpb25zIFN0YW5kYXJkaXplZCBCeSBTcGVjaWVzIC0gJywgc3BlY2llcywgJy5wZGYnLCBzZXAgPSAnJyksIHBhdGggPSBmaWd1cmVfZGlyZWN0b3J5KQp9CgpgYGAKCiMjIyBCYXIgcGxvdCB2ZXNzZWwgdHJhZmZpYyBieSBkYXRlCmBgYHtyfQojIyMgTE9HSUMgSEVSRSBUTyBHRVQgVE8gIyBCT0FUUyAvIERBWQojIyBHZXQgbWF4X3Zlc3NlbHMgYXQgYW55IGdpdmVuIHRpbWUsIHRvdGFsX3Zlc3NlbHMKCgojIE1ha2UgcGxvdCBmb3IgbWF4X3Zlc3NlbHMgCm1heF92ZXNzZWxzX3Bsb3QgPSBnZ3Bsb3QoZGF0YSA9IHZlc3NlbHNfcGVyX2RheSwgbWFwcGluZyA9IGFlcyh4ID0gZGF0ZSwgeSA9IG1heF92ZXNzZWxzKSkgKyAKICAgIGdlb21fYmFyKHN0YXQgPSAnaWRlbnRpdHknKSArIAogICAgbGFicyh0aXRsZSA9ICdNYXhpbXVtIE51bWJlciBvZiBDby1vY2N1cmluZyBWZXNzZWxzIERhaWx5JywgeCA9ICdEYXRlJywgeSA9ICcjIG9mIFZlc3NlbHMnKSArCiAgICBnZ3NhdmUoZmlsZW5hbWUgPSBwYXN0ZSgnTWF4aW11bSBOdW1iZXIgb2YgQ28tb2NjdXJpbmcgVmVzc2VscyBEYWlseS5wZGYgJywgc3BlY2llcywgJy5wZGYnLCBzZXAgPSAnJyksIHBhdGggPSBmaWd1cmVfZGlyZWN0b3J5KQoKIyBNYWtlIHBsb3QgZm9yIHRwdGFsX3Zlc3NlbHMgCnRvdGFsX3Zlc3NlbHNfcGxvdCA9IGdncGxvdChkYXRhID0gdmVzc2Vsc19wZXJfZGF5LCBtYXBwaW5nID0gYWVzKHggPSBkYXRlLCB5ID0gdG90YWxfdmVzc2VscykpICsgCiAgICBnZW9tX2JhcihzdGF0ID0gJ2lkZW50aXR5JykgKyAKICAgIGxhYnModGl0bGUgPSAnTWF4aW11bSBOdW1iZXIgb2YgQ28tb2NjdXJpbmcgVmVzc2VscyBEYWlseScsIHggPSAnRGF0ZScsIHkgPSAnIyBvZiBWZXNzZWxzJykgKwogICAgZ2dzYXZlKGZpbGVuYW1lID0gcGFzdGUoJ1RvdGFsIFZlc3NlbHMgRGFpbHkucGRmICcsIHNwZWNpZXMsICcucGRmJywgc2VwID0gJycpLCBwYXRoID0gZmlndXJlX2RpcmVjdG9yeSkKCnByaW50KG1heF92ZXNzZWxzX3Bsb3QpCnByaW50KHRvdGFsX3Zlc3NlbHNfcGxvdCkKYGBgCgoKIyMjIFNjYXR0ZXIgcGxvdCB4IGF4aXMgYm9hdCB0cmFmZmljLCB5IGF4aXMgcHJlc2VuY2UgLyBhYnNlbmNlIGNvbG9yIGJ5IHNwcApgYGB7cn0KCmBgYAoKIyMjIFNjYXR0ZXIgcGxvdCB4IGF4aXMgYm9hdCB0cmFmZmljLCB5IGF4aXMgZGV0ZWN0aW9ucyBwZXIgaW5kaXZpZHVhbCBjb2xvciBieSBzcHAgYWRkIGVycm9yIGJhcnMgZm9yIGRhaWx5IGRldGVjdGlvbnMKYGBge3J9CgpgYGAKCiMgUmVzaWRlbmN5IGFuZCBkaXNwZXJzYWwKYGBge3J9CiMjIENhbGN1bGF0ZSByZXNpZGVuY3kKZGV0ZWN0aW9uX3N0YXRzJHJlc2lkZW5jZV9tZXRyaWMgPSBkZXRlY3Rpb25fc3RhdHMkdW5pcXVlX2RheXMgLyBkZXRlY3Rpb25fc3RhdHMkZGF5c19hdF9saWJlcnR5CgojIyBBc3NpZ24gcmVzaWRlbmNlIGNhdGVnb3J5OiBsb3cgPSA8IDMzJSwgbWVkaXVtID0gMzMgLSA2NiwgaGlnaCA9ID49IDY2IChUaW5oYW4gZXQgYWwuIDIwMTQpIC0KZGV0ZWN0aW9uX3N0YXRzJHJlc2lkZW5jZV9jYXRlZ29yeSA9ICdMb3cnCmZvciAoaSBpbiAxOm5yb3coZGV0ZWN0aW9uX3N0YXRzKSl7CiAgaWYgKGRldGVjdGlvbl9zdGF0cyRyZXNpZGVuY2VfbWV0cmljW2ldID49ICgxLzMpKSB7CiAgICBkZXRlY3Rpb25fc3RhdHMkcmVzaWRlbmNlX2NhdGVnb3J5W2ldID0gJ01lZGl1bScKICB9CiAgaWYgKGRldGVjdGlvbl9zdGF0cyRyZXNpZGVuY2VfbWV0cmljW2ldID49ICgyLzMpKSB7CiAgICBkZXRlY3Rpb25fc3RhdHMkcmVzaWRlbmNlX2NhdGVnb3J5W2ldID0gJ0hpZ2gnCiAgfQp9CgojIyBDcmVhdGUgZ3JvdXBlZCBiYXJwbG90IG9mIHJlc2lkZW5jeSBieSBzcGVjaWVzCmdncGxvdChkYXRhID0gZXhwZXJpbWVudCwgbWFwcGluZyA9IGFlcyh4PWRhdGUsIHk9Y2FyX2NvdW50LCBmaWxsPXNpdGUpKSArCiAgZ2VvbV9iYXIoc3RhdD0iaWRlbnRpdHkiLCBwb3NpdGlvbiA9ICJkb2RnZSkKCmBgYAoKIyMgQ2FsY3VsYXRlIDMwIGRheSBtb3ZpbmcgYXZlcmFnZSBvZiByZXNpZGVuY3ksIHRoZW4gcGxvdCBhZ2FpbnN0IGRheXMgc2luY2UgdGFnZ2luZwpgYGB7cn0KIyMgR2V0IHRvdGFsIGRheXMgaW4gdGhlIHN0dWR5CnRvdGFsX2RheXNfaW5fc3R1ZHkgPSBhcy5udW1lcmljKGRpZmYuRGF0ZShjKG1pbihtb2xvX2RmJGRhdGUpLCBtYXgobW9sb19kZiRkYXRlKSkpKQoKIyMgQ3JlYXRlIGEgZGF0YWZyYW1lIHdoZXJlIHJvd3MgYXJlIHRhZyBpZCBhbmQgY29sdW1ucyBhcmUgc3R1ZHkgZGF0ZQpwcmVzZW50X2FmdGVyX25fZGF5c19kZiA9IGRhdGEuZnJhbWUoKQoKIyMgRGV0ZXJtaW5lIGlmIGEgdGFnIHdhcyBkZXRlY3RlZCBvbiBhIHJlY2VpdmVyIG4gZGF5cyBhZnRlciB0YWdnaW5nCmZvciAoaSBpbiAxOnVuaXF1ZU4obW9sb19kZiR0YWdfaWQpKXsKICAjIyBTdWJzZXQgZGF0YSBmb3IgaW5kaXZpZHVhbCB0YWdzCiAgaW5kdl9kYXRhID0gbW9sb19kZlttb2xvX2RmJHRhZ19pZCA9PSB1bmlxdWUobW9sb19kZiR0YWdfaWQpW2ldLCBdCiAgIyMgRGV0ZXJtaW5lIGlmIGEgZmlzaCB3YXMgcHJlc2VudCBuIGRheXMgYWZ0ZXIgdGFnZ2luZwogIGRpZmZ0aW1lcyA9IHJlcCgwLCBsZW4gPSB0b3RhbF9kYXlzX2luX3N0dWR5KQogICMgZGV0ZXJtaW5lIGRpZmZlcmVuY2UgaW4gZGF5cyBiZXR3ZWVuIGVhY2ggdW5pcXVlIGRheSBhIHRhZyB3YXMgZGV0ZWN0ZWQgYW5kIHRoZSB0YWcncyBlYXJsaWVzdCBkZXRlY3Rpb24sIGZsaXAgdGhlIGNvcnJlc3BvbmRpbmcgdmFsdWUgaW4gZGlmZnRpbWVzIGFycmF5IHRvIDEKICBkZXRlY3RlZF9kYXRlcyA9IHVuaXF1ZShpbmR2X2RhdGEkZGF0ZSkKICBmb3IgKGogaW4gMTpsZW5ndGgoZGV0ZWN0ZWRfZGF0ZXMpKXsKICAgIGRpZmZ0aW1lc1thcy5udW1lcmljKGRpZmYuRGF0ZShjKG1pbihpbmR2X2RhdGEkZGF0ZSksIGRldGVjdGVkX2RhdGVzW2pdKSkpICsgMV0gPSAxCiAgfQogIGRmX3JvdyA9IGModW5pcXVlKG1vbG9fZGYkdGFnX2lkKVtpXSwgZGlmZnRpbWVzKQogIHByZXNlbnRfYWZ0ZXJfbl9kYXlzX2RmID0gcmJpbmQocHJlc2VudF9hZnRlcl9uX2RheXNfZGYsIGRmX3JvdykKfQpjb2xuYW1lcyhwcmVzZW50X2FmdGVyX25fZGF5c19kZikgPSBjKCd0YWdfaWQnLCBhcy5jaGFyYWN0ZXIoMTp0b3RhbF9kYXlzX2luX3N0dWR5KSkKCiMjIENvbnZlcnQgZnJvbSB3aWRlIGZvcm1hdCB0byBsb25nIGZvcm1hdApwcmVzZW50X2FmdGVyX25fZGF5c19kZl9sb25nX2RmID0gbWVsdChwcmVzZW50X2FmdGVyX25fZGF5c19kZiwgaWQudmFycyA9ICd0YWdfaWQnLCBtZWFzdXJlLnZhcnMgPSBjb2xuYW1lcyhwcmVzZW50X2FmdGVyX25fZGF5c19kZilbMjpuY29sKHByZXNlbnRfYWZ0ZXJfbl9kYXlzX2RmKV0sIHZhcmlhYmxlLm5hbWUgPSAnZGF5JywgdmFsdWUubmFtZSA9ICdkZXRlY3RlZCcpCgojIyBNZXJnZSB3aXRoIHNwZWNpZXMgZGF0YQpwcmVzZW50X2FmdGVyX25fZGF5c19kZl9sb25nX2RmID0gbWVyZ2UoeCA9IHByZXNlbnRfYWZ0ZXJfbl9kYXlzX2RmX2xvbmcrZGYsIHkgPSB0YWdnaW5nX2RmWyAsYygndGFnX2lkJywgJ3NwZWNpZXMnKV0sIG9uID0gJ3RhZ19pZCcpCgojIyBDYWxjdWxhdGUgbnVtYmVyIG9mIGVhY2ggc3BlY2llcyBwcmVzZW50IG4gZGF5cyBhZnRlciB0YWdnaW5nCnNwZWNpZXNfcHJlc2VuY2VfYWZ0ZXJfdGFnZ2luZyA9IGFnZ3JlZ2F0ZShkZXRlY3RlZCB+IHNwZWNpZXMgKyBkYXksIGRhdGEgPSBwcmVzZW50X2FmdGVyX25fZGF5c19kZl9sb25nLCBGVU4gPSBzdW0pCiAgY29sbmFtZXMoc3BlY2llc19wcmVzZW5jZV9hZnRlcl90YWdnaW5nKSA9IGMoJ3NwZWNpZXMnLCAnZGF5JywgJ25faW5kaXZpZHVhbHMnKQoKIyMgQ291bnQgdW5pcXVlIHRhZ3MgYnkgc3BlY2llcwppbmRpdmlkdWFsc19wZXJfc3BlY2llcyA9IGFnZ3JlZ2F0ZSh0YWdfaWQgfiBzcGVjaWVzLCBkYXRhID0gcHJlc2VudF9hZnRlcl9uX2RheXNfZGZfbG9uZywgRlVOID0gdW5pcXVlTikKY29sbmFtZXMoaW5kaXZpZHVhbHNfcGVyX3NwZWNpZXMpID0gYygnc3BlY2llcycsICduX3RhZ3MnKQoKIyMgU3RhbmRhcmRpemUgc3BlY2llcyBsZXZlbCBkYWlseSBjb3VudHMgYnkgbnVtYmVyIG9mIHRhZ3MgYmVsb25naW5nIHRvIHRoYXQgc3BlY2llcwpzcGVjaWVzX3ByZXNlbmNlX2FmdGVyX3RhZ2dpbmcgPSBtZXJnZSh4ID0gc3BlY2llc19wcmVzZW5jZV9hZnRlcl90YWdnaW5nLCB5ID0gaW5kaXZpZHVhbHNfcGVyX3NwZWNpZXMsIG9uID0gJ3NwZWNpZXMnKQpzcGVjaWVzX3ByZXNlbmNlX2FmdGVyX3RhZ2dpbmckcGVyY2VudF9pbmRpdmlkdWFsc19kZXRlY3RlZCA9IHNwZWNpZXNfcHJlc2VuY2VfYWZ0ZXJfdGFnZ2luZyRuX2luZGl2aWR1YWxzIC8gc3BlY2llc19wcmVzZW5jZV9hZnRlcl90YWdnaW5nJG5fdGFncwoKIyMgQ2FsY3VsYXRlIDMwIGRheSBtb3ZpbmcgYXZlcmFnZQpzcHBfcHJlc2VuY2VfMzBfZGF5X2F2ZyA9IGRhdGEuZnJhbWUoKQpmb3IgKHNwZWNpZXMgaW4gc3BlY2llc19wcmVzZW5jZV9hZnRlcl90YWdnaW5nJHNwZWNpZXMpewogIHNwcF9wcmVzZW5jZV9hZnRlcl90YWdnaW5nID0gc3BlY2llc19wcmVzZW5jZV9hZnRlcl90YWdnaW5nW3NwZWNpZXNfcHJlc2VuY2VfYWZ0ZXJfdGFnZ2luZyRzcGVjaWVzID09IHNwZWNpZXMsIF0KICBtb3ZpbmdfYXZlcmFnZV8zMCA9IGMoKQogIGZvciAoaSBpbiAzMDptYXgoc3BlY2llc19wcmVzZW5jZV9hZnRlcl90YWdnaW5nJGRheXMpKXsKICAgIG1vdmluZ19hdmVyYWdlXzMwID0gYyhtb3ZpbmdfYXZlcmFnZV8zMCwgbWVhbihzcGVjaWVzX3ByZXNlbmNlX2FmdGVyX3RhZ2dpbmckcGVyY2VudF9pbmRpdmlkdWFsc19kZXRlY3RlZFtzcGVjaWVzX3ByZXNlbmNlX2FmdGVyX3RhZ2dpbmckZGF5ID49IGktMzAgJiBzcGVjaWVzX3ByZXNlbmNlX2FmdGVyX3RhZ2dpbmckZGF5IDw9IGldKSkKICB9CiAgZGZfcm93ID0gYyhzcGVjaWVzLCBtb3ZpbmdfYXZlcmFnZV8zMCkKICBzcHBfcHJlc2VuY2VfMzBfZGF5X2F2ZyA9IHJiaW5kKHNwcF9wcmVzZW5jZV8zMF9kYXlfYXZnLCBkZl9yb3cpCn0KY29sbmFtZXMoc3BwX3ByZXNlbmNlXzMwX2RheV9hdmcpID0gYygnc3BlY2llcycsIGFzLmNoYXJhY3RlcigxOihuY29sKHNwcF9wcmVzZW5jZV8zMF9kYXlfYXZnKS0xKSkpCgojIyBDb252ZXJ0IGZyb20gd2lkZSBmb3JtYXQgdG8gbG9uZyBmb3JtYXQKc3BwX3ByZXNlbmNlXzMwX2RheV9hdmdfbG9uZ19kZiA9IG1lbHQoc3BwX3ByZXNlbmNlXzMwX2RheV9hdmcsIGlkLnZhcnMgPSAnc3BlY2llcycsIG1lYXN1cmUudmFycyA9IGNvbG5hbWVzKHNwcF9wcmVzZW5jZV8zMF9kYXlfYXZnKVsyOm5jb2woc3BwX3ByZXNlbmNlXzMwX2RheV9hdmcpXSwgdmFyaWFibGUubmFtZSA9ICdkYXknLCB2YWx1ZS5uYW1lID0gJ3BlcmNlbnRfaW5kaXZpZHVhbHNfZGV0ZWN0ZWQnKQoKIyMgR2VuZXJhdGUgbGluZSBwbG90CnByZXNlbnRfYWZ0ZXJfdGFnZ2luZ19wbG90ID0gZ2dwbG90KHNwcF9wcmVzZW5jZV8zMF9kYXlfYXZnX2xvbmdfZGYsIG1hcHBpbmcgPSBhZXMoeCA9IGRheSwgeSA9IHBlcmNlbnRfaW5kaXZpZHVhbHNfZGV0ZWN0ZWQsIGNvbG9yID0gc3BlY2llcykpICsgCiAgZ2VvbV9saW5lKCkgKyAKICBsYWJzKHggPSAnTnVtYmVyIG9mIGRheXMnLCB5ID0gJ1Byb3BvcnRpb24gcHJlc2VudCcpICsKICBnZ3NhdmUoZmlsZW5hbWUgPSAnUHJvcG9ydGlvbiBvZiB0YWdzIHByZXNlbnQgYWZ0ZXIgdGFnZ2luZy5wZGYnLCBwYXRoID0gZmlndXJlX2RpcmVjdG9yeSkKCnByaW50KHByZXNlbnRfYWZ0ZXJfdGFnZ2luZ19wbG90KQpgYGAKCiMgU3RhdGlzdGljYWwgYW5hbHlzaXMKCkNhbGN1bGF0ZSBtZWFuIHJlc2lkZW5jeSBieSBzcHAgKGlycmVnYXJkbGVzcyBvZiB0aW1lKSwgdGhlbiBBTk9WQSBieSBzcHAKVXNlIFR1a2V5J3MgSFNEIHRvIGRldGVybWluZSBzaWduaWZpY2FuY2UKYGBge3J9CiMjIEFOT1ZBIG1vZGVsIGZvciByZXNpZGVuY3kgbWV0cmljIGJ5IHNwZWNpZXMKcmVzaWRlbmNlX2J5X3NwZWNpZXNfYW5vdmEgPSBhb3YocmVzaWRlbmNlX21ldHJpYyB+IHNwZWNpZXMsIGRhdGE9ZGV0ZWN0aW9uX3N0YXRzKQpzdW1tYXJ5KHJlc2lkZW5jZV9ieV9zcGVjaWVzX2Fub3ZhKQoKIyMgVHVrZXkncyBIb25lc3RseSBTaWduaWZpY2FudCBEaWZmZXJlbmNlcyBiZXR3ZWVuIHNwZWNpZXMKVHVrZXlIU0QocmVzaWRlbmNlX2J5X3NwZWNpZXNfYW5vdmEpCmBgYAoKR0xNIGNvbXBhcmluZyBzaXplIGFuZCByZXNpZGVuY3kgdGltZSBieSBzcHAgaW5kZXBlbmRlbnQgdmFyIChzaXplLCB0aW1lIGF0IGxpYmVydHkpIGRlcGVuZGVudCAocmVzaWRlbmN5IGluZGV4KQpgYGB7cn0KIyMgRml0IGJpbm9taWFsIEdMTSB0byBhdmVyYWdlIHJlc2lkZW5jeSBtZXRyaWMgZGF0YSAocHJvcG9ydGlvbmFsIGJldHdlZW4gMC0xKQpzcGVjaWVzX2dsbSA9IGdsbShyZXNpZGVuY2VfbWV0cmljIH4gIHNwZWNpZXMgKyBmb3JrX2xlbmd0aF9jbSAqZGF5c19hdF9saWJlcnR5ICogc3BlY2llcywgZGF0YSA9IGRldGVjdGlvbl9zdGF0cywgZmFtaWx5ID0gYmlub21pYWwobG9naXQpKQpzdW1tYXJ5KHNwZWNpZXNfZ2xtKQpgYGAKCiMjIEdMTSBjb21wYXJpbmcgdGltZSBpbiBjcmF0ZXIgdG8gdmVzc2VsIHRyYWZmaWMKYGBge3J9CgpgYGA=